package com.zx.framework.db;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.transform.Transformers;
import org.springframework.orm.hibernate4.HibernateCallback;
import org.springframework.orm.hibernate4.support.HibernateDaoSupport;

import com.zx.framework.core.pagination.Pagination;
import com.zx.framework.core.util.ObjectUtils;
import com.zx.framework.db.exception.DatabaseException;

/**
 * 用于实现hibernate作为持久手段的dao
 * 
 * @author stylewolf
 * 
 */
public class HibernateDAO extends HibernateDaoSupport implements IBaseDAO {

	private static final Logger LOG = Logger.getLogger(HibernateDAO.class);

	@Override
	public void setParameters(Query qry, Object... params) {
		if (params != null) {
			for (int i = 0; i < params.length; i++) {
				Object p = params[i];
				//如果是键值对的形式，则判断值是否为集合，根据是否集合，调用对应的setParameterList、setParameter方法
				if(p instanceof KeyValue){
					KeyValue keyValue = (KeyValue) p;
					if(keyValue.getValue() instanceof Collection){
						qry.setParameterList(keyValue.getKey(), (Collection) keyValue.getValue());
					}
					else{
						qry.setParameter(keyValue.getKey(), keyValue.getValue());
					}
				}else{
					qry.setParameter(i, p);
				}
			}
		}
	}

	@Override
	public <T, PK extends Serializable> T findById(PK id, Class<T> type) {
		try {
			if(ObjectUtils.isNull(id)){
				throw new DatabaseException("主键参数不能为空！");
			}
			return getHibernateTemplate().get(type, id);
		} catch (Exception e) {
			LOG.error("执行"+type.getName()+"数据查询失败！", e);
			throw new DatabaseException("执行数据查询失败！");
		}
	}

	@Override
	public void remove(Object entity) {
		try {
			getHibernateTemplate().delete(entity);
		} catch (Exception e) {
			LOG.error("执行数据删除失败！", e);
			throw new DatabaseException("数据删除失败");
		}	
	}

	@Override
	public void save(Object entity) {
		try {
			getHibernateTemplate().save(entity);
		} catch (Exception e) {
			LOG.error("执行数据保存失败！", e);
			throw new DatabaseException("执行数据保存失败！");
		}	
	}

	@Override
	public void update(Object entity) {
		try {
			getHibernateTemplate().update(entity);
		} catch (Exception e) {
			LOG.error("执行数据更新失败！", e);
			throw new DatabaseException("执行数据更新失败！");
		}
	}

	@Override
	public void merge(Object entity) {
		try {
			getHibernateTemplate().merge(entity);
		} catch (Exception e) {
			LOG.error("执行数据更新失败！", e);
			throw new DatabaseException("执行数据更新失败！");
		}
	}

	@Override
	public void saveOrUpdate(Object entity) {
		try {
			getHibernateTemplate().saveOrUpdate(entity);
		} catch (Exception e) {
			LOG.error("执行数据更新失败！", e);
			throw new DatabaseException("执行数据更新失败！");
		}
	}

	@Override
	public int executeSQL(final String sql,final Object... params) {
		try {
			return getHibernateTemplate().execute(
					new HibernateCallback<Integer>() {

						@Override
						public Integer doInHibernate(Session session)
								throws HibernateException {
							SQLQuery qry = session.createSQLQuery(sql);
							setParameters(qry, params);
							return qry.executeUpdate();
						}
					});
		} catch (Exception e) {
			LOG.error("执行executeSQL语句失败！", e);
			throw new DatabaseException("执行数据更新失败！");
		}
	}

	@Override
	public int executeHQL(final String hql,final Object... params) {
		try {
			return getHibernateTemplate().execute(
					new HibernateCallback<Integer>() {

						@Override
						public Integer doInHibernate(Session session)
								throws HibernateException {
							Query qry = session.createQuery(hql);
							setParameters(qry, params);
							return qry.executeUpdate();
						}
					});
		} catch (Exception e) {
			LOG.error("执行execHQL数据更新失败！", e);
			throw new DatabaseException("执行数据更新失败！");
		}
	}

	@Override
	public <E> List<E> baseQueryHQL(final String hql,final  int start,final int size,
			final Object... params) {
		try {
			return getHibernateTemplate().execute(
					new HibernateCallback<List<E>>() {
						@Override
						public List<E> doInHibernate(Session session)
								throws HibernateException {
							Query qry = session.createQuery(hql);
							setParameters(qry, params);
							//用于分页或者取特定个数的限制
							if (start >= 0 && size > 0) {
								qry.setFirstResult(start);
								qry.setMaxResults(size);
							}
							return qry.list();
						}
					});
		} catch (Exception e) {
			LOG.error("执行queryHQL数据查询失败！", e);
			throw new DatabaseException("执行数据查询失败！");
		}
	}

	@Override
	public <E> Pagination<E> hqlPageQuery(String qryHql, String cntHql,
			int current, int size, Object... params) {
		
		int count = getHQLUnique(cntHql, Long.class, params).intValue();
		int pageStart = 0;
		Pagination<E> page = new Pagination<E>();
		page.initPagination(count, size, current);
		if (page.getPage() > 0 && page.getRows() > 0)
		{
			pageStart = page.getRows() * (page.getPage()  - 1);
		}
		List<E> datas = baseQueryHQL(qryHql, pageStart, size, params);
		
		page.setDatas(datas);
		return page;
	}

	@Override
	public <E> List<E> queryHQL(String hql, Object... params) {
		return baseQueryHQL(hql, -1, -1, params);
	}

	@Override
	public <E> List<E> baseQuerySQL(final String sql,final  int start,final int size,
			final Object... params) {
		try {
			return getHibernateTemplate().execute(
					new HibernateCallback<List>() {

						@Override
						public List doInHibernate(Session session)
								throws HibernateException {
							Query qry = session.createSQLQuery(sql);
							setParameters(qry, params);
							qry.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
							if (start >= 0 && size > 0) {
								qry.setFirstResult(start);
								qry.setMaxResults(size);
							}
							return qry.list();
						}
					});
		} catch (Exception e) {
			LOG.error("执行querySQL数据查询失败！", e);
			throw new DatabaseException("执行数据查询失败！");
		}
	}

	@Override
	public <E> Pagination<E> sqlPageQuery(String qrySql, String cntSql,
			int current, int size, Object... params) {
		
		Object countObj = getSQLUnique(cntSql, Object.class, params);
		int count = ObjectUtils.getIntValue(countObj);
		
		int pageStart = 0;
		
		if (size > 0 && current > 0)
		{
			pageStart = size * (current - 1);
		}
		List<E> datas =baseQuerySQL(qrySql, pageStart, size, params);
		Pagination<E> page = new Pagination<E>(count,size,current,datas);
		return page;
	}

	@Override
	public <E> List<E> querySQL(String sql, Object... params) {
		return baseQuerySQL(sql, -1, -1, params);
	}

	@Override
	public <E> List<E> findByProperty(String entityName,
			String propertyName, Object value, String... orders) {
		return findByProperty(entityName, new KeyValue(propertyName, value), orders);
	}

	@Override
	public <E> List<E> findByProperty(String entityName,
			KeyValue queryParam, String... orders) {
		List<KeyValue> queryParams = new ArrayList<KeyValue>();
		queryParams.add(queryParam);
		return findByPropertys(entityName, queryParams, -1, -1, orders);
	}

	@Override
	public <E> List<E> findByProperty(String entityName,
			String propertyName, Object value) {
		return findByProperty(entityName, propertyName, value, null);
	}

	@Override
	public <E> List<E> findByProperty(String entityName, KeyValue queryParam) {
		List<KeyValue> queryParams = new ArrayList<KeyValue>();
		queryParams.add(queryParam);
		return findByPropertys(entityName, queryParams, -1, -1, null);
	}

	@Override
	public <E> List<E> findByPropertys(final String tableName,final List<KeyValue> queryParams,
			final int start,final int size,final String... orders) {
		try {
			return getHibernateTemplate().execute(
					new HibernateCallback<List<E>>() {

						@Override
						public List<E> doInHibernate(Session session)
								throws HibernateException {
							String queryString = "from " + tableName
									+ " as model ";
							Object[] params = null;
							if (queryParams != null && !queryParams.isEmpty()) {
								queryString += " where ";
								params = new Object[queryParams.size()];
								for (int i = 0; i < queryParams.size(); i++) {
									KeyValue kv = queryParams.get(i);
									params[i] = kv;
									queryString += " model."
											+ queryParams.get(i).getKey() + "= :"+kv.getKey();
									if (i != queryParams.size() - 1) {
										queryString += " and ";
									}
								}
							}
							if (orders != null && orders.length>0) {
								queryString += " order by ";
								for (int i = 0; i < orders.length; i++) {
									queryString += " model."
											+ orders[i];
									if (i != orders.length - 1) {
										queryString += ", ";
									}
								}
							}
							Query queryObject = session.createQuery(
									queryString);
							setParameters(queryObject, params);
							if (start >= 0 && size > 0) {
								queryObject.setFirstResult(start);
								queryObject.setMaxResults(size);
							}
							return queryObject.list();
						}
					});
		} catch (Exception e) {
			LOG.error("执行[" + tableName + "]数据查询失败！", e);
			throw new DatabaseException("执行数据查询失败！");
		}
	}

	@Override
	public <E> List<E> findByPropertys(String tableName,
			List<KeyValue> queryParams, String... orders) {
		return findByPropertys(tableName, queryParams, -1, -1, orders);
	}

	@Override
	public <E> List<E> findAll(String tableName) {
		return findByPropertys(tableName, null);
	}

	@Override
	public <E> List<E> findAll(String tableName, String... orders) {
		return findByPropertys(tableName, null, orders);
	}

	@Override
	public <T> T getHQLUnique(final String hql,final Class<T> type,final Object... params) {
		try {
			return getHibernateTemplate().execute(new HibernateCallback<T>() {

				@Override
				public T doInHibernate(Session session)
						throws HibernateException {
					Query qry = session.createQuery(hql);
					setParameters(qry, params);
					qry.setMaxResults(1);
					Object o = qry.uniqueResult();
					return type.cast(o);
				}
			});
		} 
		catch (ClassCastException e) {
			LOG.error("执行HQL数据统计查询失败,数据类型转换失败！", e);
			throw new DatabaseException("执行数据统计查询失败！");
		}
		catch (Exception e) {
			LOG.error("执行HQL数据统计查询失败！", e);
			throw new DatabaseException("执行数据统计查询失败！");
		}
	}

	@Override
	public <T> T getSQLUnique(final String sql,final Class<T> type,final Object... params) {
		try {
			return getHibernateTemplate().execute(new HibernateCallback<T>() {

				@Override
				public T doInHibernate(Session session)
						throws HibernateException {
					Query qry = session.createSQLQuery(sql);
					setParameters(qry, params);
					qry.setMaxResults(1);
					return type.cast(qry.uniqueResult());
				}
			});
		} 
		catch (ClassCastException e) {
			LOG.error("执行SQL数据统计查询失败,数据类型转换失败！", e);
			throw new DatabaseException("执行数据统计查询失败！");
		}
		catch (Exception e) {
			LOG.error("执行SQL数据统计查询失败！", e);
			throw new DatabaseException("执行数据统计查询失败！");
		}
	}

	@Override
	public int bulkUpdate(String updateSql, Object... params) {
		return getHibernateTemplate().bulkUpdate(updateSql, params);
	}

	@Override
	public void flush() {
		getHibernateTemplate().flush();
	}
}
